package de.ipk_gatersleben.bit.bi.edal.publication;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Calendar;
import java.util.Date;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import de.ipk_gatersleben.bit.bi.edal.primary_data.metadata.EdalDate;
import de.ipk_gatersleben.bit.bi.edal.rmi.client.util.Const;
import de.ipk_gatersleben.bit.bi.edal.rmi.client.util.ImageUtil;

/**
 * <code>DatetimePicker</code> provides a swing panel for the user to choose
 * Datetime.
 * <p>
 * 
 * @version 2.0
 * @author chenj
 * @author arendd
 * 
 */
public class DateTimePicker extends JPanel {
	private static final long serialVersionUID = 1L;
	private static final Font FONT = new Font("Courier New", Font.PLAIN, 12);
	private DateChooser dateChooser = null;
	private Calendar showcalendar;
	private Calendar calendar;
	private JTextField text;
	private JLabel label;

	private static final String CENTURY = "Century";
	private static final String DECADE = "Decade";
	private static final String YEAR = "Year";
	private static final String MONTH = "Month";
	private static final String DAY = "Day";
	private static final String HOUR = "Hour";
	private static final String MINUTE = "Minute";
	private static final String SECOND = "Second";
	private static final String MILLISECOND = "Millisecond";

	/**
	 * Constructs a <code>DatetimePicker</code> that is initialized with
	 * <code>edaldate</code>. If the parameter are <code>null</code> this method
	 * will initialize the DatetimePicker with current time
	 * 
	 * @param edaldate
	 *            EDALDate object to show in DatetimePicker panel
	 */

	public DateTimePicker(EdalDate edaldate) {
		if (edaldate != null) {
			this.showcalendar = edaldate.getStartDate();
		} else {
			this.showcalendar = Calendar.getInstance();
		}
		setBorder(null);
		setCursor(new Cursor(Cursor.HAND_CURSOR));

		GridBagLayout gridBagLayout = new GridBagLayout();

		setLayout(gridBagLayout);

		text = new JTextField();
		text.setHorizontalAlignment(JTextField.CENTER);
		text.setEditable(false);

		ImageIcon icon = ImageUtil.createImageIcon("dateIcon.gif",
				"choose new datetime");
		label = new JLabel(icon);
		label.setToolTipText("choose new datetime");

		addComponent(this, gridBagLayout, text, 0, 0, 1, 1, 0.95, 1, 1, 1);
		addComponent(this, gridBagLayout, label, 1, 0, 1, 1, 0.05, 1, 1, 1);

		label.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (dateChooser == null) {
					dateChooser = new DateChooser();
				}
				Point p = getLocationOnScreen();
				p.y = p.y + 30;
				dateChooser.showDateChooser(p);
			}
		});
	}

	static void addComponent(Container cont, GridBagLayout gbl, Component c,
			int x, int y, int width, int height, double weightx,
			double weighty, int ipadx, int ipady) {
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.ipadx = ipadx;
		gbc.ipady = ipady;
		gbc.gridx = x;
		gbc.gridy = y;
		gbc.gridwidth = width;
		gbc.gridheight = height;
		gbc.weightx = weightx;
		gbc.weighty = weighty;
		gbc.insets = new Insets(0, 0, 0, 0);
		gbl.setConstraints(c, gbc);
		cont.add(c);
	}

	/**
	 * reset the time to current time
	 */
	public void reset() {
		this.showcalendar = Calendar.getInstance();
		text.setText("");
	}

	/**
	 * Returns the Calendar, which is selected by user.
	 * 
	 * @return the Calendar, which is selected by user.
	 */
	public Calendar getCalendar() {
		return this.calendar;
	}

	private void setDate(Date date) {
		text.setText(date.toString());
	}

	public Date getDate() {
		return this.showcalendar.getTime();
	}

	public class DateChooser extends JPanel implements ActionListener,
			ChangeListener, MouseListener {
		private static final long serialVersionUID = 1L;
		int startYear = 1;
		int lastYear = 5000;
		int width = 750;
		int height = 280;

		Color backGroundColor = Color.gray;
		Color palletTableColor = Color.white;
		Color todayBackColor = Color.orange;
		Color weekFontColor = Color.blue;
		Color dateFontColor = Color.black;
		Color weekendFontColor = Color.red;
		Color controlLineColor = Color.pink;
		Color controlTextColor = Color.white;

		JDialog dialog;
		JSpinner yearSpinner;
		JSpinner monthSpinner;
		JSpinner daySpinner;
		JSpinner hourSpinner;
		JSpinner minuteSpinner;
		JSpinner secondSpinner;
		JSpinner millisecondSpinner;

		JButton[][] daysButton = new JButton[6][7];

		public DateChooser() {
			setLayout(new BorderLayout());
			setBorder(new LineBorder(backGroundColor, 2));
			setBackground(backGroundColor);

			JPanel mainPane = new JPanel();
			mainPane.setLayout(new BorderLayout());

			JPanel controlpanel = createYearAndMonthPanel();
			mainPane.add(controlpanel, BorderLayout.CENTER);
			JPanel displaypanel = createWeekAndDayPanel();
			mainPane.add(displaypanel, BorderLayout.SOUTH);

			JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.CENTER, 5,
					5));
			JButton savebtn = new JButton(okAction);
			JButton cancelbtn = new JButton(cancelAction);
			buttonPane.add(savebtn);
			buttonPane.add(cancelbtn);

			add(mainPane, BorderLayout.CENTER);
			add(buttonPane, BorderLayout.SOUTH);
		}

		private JPanel createYearAndMonthPanel() {
			Calendar c = getCalendar();
			int currentYear = c.get(Calendar.YEAR);
			int currentMonth = c.get(Calendar.MONTH) + 1;
			int currentday = c.get(Calendar.DAY_OF_MONTH);
			int currentHour = c.get(Calendar.HOUR_OF_DAY);
			int currentMinute = c.get(Calendar.MINUTE);
			int currentSecond = c.get(Calendar.SECOND);
			int currentMillisecond = c.get(Calendar.MILLISECOND);

			JPanel result = new JPanel();
			result.setLayout(new FlowLayout());
			result.setBackground(controlLineColor);

			JLabel yearLabel = new JLabel(YEAR);
			yearLabel.setForeground(controlTextColor);
			result.add(yearLabel);

			yearSpinner = new JSpinner(new SpinnerNumberModel(currentYear,
					startYear, lastYear, 1));
			yearSpinner.setPreferredSize(new Dimension(64, 24));
			yearSpinner.setName(YEAR);
			yearSpinner
					.setEditor(new JSpinner.NumberEditor(yearSpinner, "####"));
			yearSpinner.addChangeListener(this);
			result.add(yearSpinner);

			JLabel monthLabel = new JLabel(MONTH);
			monthLabel.setForeground(controlTextColor);
			result.add(monthLabel);

			monthSpinner = new JSpinner(new SpinnerNumberModel(currentMonth, 1,
					12, 1));
			monthSpinner.setPreferredSize(new Dimension(50, 24));
			monthSpinner.setName(MONTH);
			monthSpinner.addChangeListener(this);
			result.add(monthSpinner);

			JLabel dayLabel = new JLabel(DAY);
			dayLabel.setForeground(controlTextColor);
			result.add(dayLabel);

			daySpinner = new JSpinner(getdaymodel(currentYear, currentMonth,
					currentday));
			daySpinner.setPreferredSize(new Dimension(50, 24));
			daySpinner.setName(DAY);
			daySpinner.addChangeListener(this);
			result.add(daySpinner);

			JLabel hourLabel = new JLabel(HOUR);
			hourLabel.setForeground(controlTextColor);
			result.add(hourLabel);

			hourSpinner = new JSpinner(new SpinnerNumberModel(currentHour, 0,
					23, 1));
			hourSpinner.setPreferredSize(new Dimension(50, 24));
			hourSpinner.setName(HOUR);
			hourSpinner.addChangeListener(this);
			result.add(hourSpinner);

			JLabel minuteLabel = new JLabel(MINUTE);
			minuteLabel.setForeground(controlTextColor);
			result.add(minuteLabel);

			minuteSpinner = new JSpinner(new SpinnerNumberModel(currentMinute,
					0, 59, 1));
			minuteSpinner.setPreferredSize(new Dimension(50, 24));
			minuteSpinner.setName(MINUTE);
			minuteSpinner.addChangeListener(this);
			result.add(minuteSpinner);

			JLabel secondLabel = new JLabel(SECOND);
			secondLabel.setForeground(controlTextColor);
			result.add(secondLabel);

			secondSpinner = new JSpinner(new SpinnerNumberModel(currentSecond,
					0, 59, 1));
			secondSpinner.setPreferredSize(new Dimension(50, 24));
			secondSpinner.setName(SECOND);
			secondSpinner.addChangeListener(this);
			result.add(secondSpinner);

			JLabel millisecondLabel = new JLabel(MILLISECOND);
			millisecondLabel.setForeground(controlTextColor);
			result.add(millisecondLabel);

			millisecondSpinner = new JSpinner(new SpinnerNumberModel(
					currentMillisecond, 0, 999, 1));
			millisecondSpinner.setPreferredSize(new Dimension(55, 24));
			millisecondSpinner.setName(MILLISECOND);
			millisecondSpinner.addChangeListener(this);
			result.add(millisecondSpinner);

			return result;
		}

		private SpinnerNumberModel getdaymodel(int currentYear,
				int currentMonth, int currentday) {
			int minday = 1;
			int maxday = 30;
			if (currentMonth == 1 || currentMonth == 3 || currentMonth == 5
					|| currentMonth == 7 || currentMonth == 8
					|| currentMonth == 10 || currentMonth == 12) {
				maxday = 31;
			} else if (currentMonth == 2) {
				if (isleapyear(currentYear)) {
					maxday = 29;
				} else {
					maxday = 28;
				}
			}
			return new SpinnerNumberModel(currentday, minday, maxday, 1);
		}

		private boolean isleapyear(int year) {
			if (year % 100 == 0) {
				if (year % 400 == 0) {
					return true;
				}
			} else {
				if (year % 4 == 0) {
					return true;
				}
			}
			return false;
		}

		private JPanel createWeekAndDayPanel() {
			String colname[] = { "Sunday", "Monday", "Tuesday ", "Wednesday",
					"Thursday", "Friday", "Saturday " };
			JPanel result = new JPanel();

			result.setFont(FONT);
			result.setLayout(new GridLayout(7, 7));
			result.setBackground(Color.white);
			JLabel cell;

			for (int i = 0; i < 7; i++) {
				cell = new JLabel(colname[i]);
				cell.setHorizontalAlignment(JLabel.CENTER);
				if (i == 0 || i == 6)
					cell.setForeground(weekendFontColor);
				else
					cell.setForeground(weekFontColor);
				result.add(cell);
			}

			int actionCommandId = 0;
			for (int i = 0; i < 6; i++)
				for (int j = 0; j < 7; j++) {
					JButton numberButton = new JButton();
					numberButton.setBorder(null);
					numberButton.setHorizontalAlignment(SwingConstants.CENTER);
					numberButton.setActionCommand(String
							.valueOf(actionCommandId));
					numberButton.addActionListener(this);
					numberButton.addMouseListener(this);
					numberButton.setBackground(palletTableColor);
					numberButton.setForeground(dateFontColor);
					if (j == 0 || j == 6)
						numberButton.setForeground(weekendFontColor);
					else
						numberButton.setForeground(dateFontColor);

					daysButton[i][j] = numberButton;
					result.add(numberButton);
					actionCommandId++;
				}

			return result;
		}

		private JDialog createDialog(Frame owner) {
			JDialog result = new JDialog(owner, "DatetimePicker", true);
			result.addWindowListener(createAppCloser());
			result.getContentPane().add(this, BorderLayout.CENTER);
			result.pack();
			result.setSize(width, height);
			return result;
		}

		private JDialog createDialog(JDialog owner) {
			JDialog result = new JDialog(owner, "DatetimePicker", true);
			result.addWindowListener(createAppCloser());
			result.getContentPane().add(this, BorderLayout.CENTER);
			result.pack();
			result.setSize(width, height);
			return result;
		}

		public void showDateChooser(Point position) {
			if (SwingUtilities.getWindowAncestor(DateTimePicker.this) != null
					&& (SwingUtilities.getWindowAncestor(DateTimePicker.this) instanceof JDialog)) {
				JDialog owner = (JDialog) SwingUtilities
						.getWindowAncestor(DateTimePicker.this);
				if (dialog == null || dialog.getOwner() != owner)
					dialog = createDialog(owner);
				dialog.setLocation(getAppropriateLocation(owner, position));
			} else {
				Frame owner = (Frame) SwingUtilities
						.getWindowAncestor(DateTimePicker.this);
				if (dialog == null || dialog.getOwner() != owner)
					dialog = createDialog(owner);
				dialog.setLocation(getAppropriateLocation(owner, position));
			}

			flushWeekAndDay();
			dialog.setVisible(true);
		}

		Point getAppropriateLocation(Frame owner, Point position) {
			Point result = new Point(position);
			Point p = owner.getLocation();
			int offsetX = (position.x + width) - (p.x + owner.getWidth());
			int offsetY = (position.y + height) - (p.y + owner.getHeight());
			if (offsetX > 0) {
				result.x -= offsetX;
			}

			if (offsetY > 0) {
				result.y -= offsetY;
			}
			return result;
		}

		Point getAppropriateLocation(JDialog owner, Point position) {
			Point result = new Point(position);
			Point p = owner.getLocation();
			int offsetX = (position.x + width) - (p.x + owner.getWidth());
			int offsetY = (position.y + height) - (p.y + owner.getHeight());
			if (offsetX > 0) {
				result.x -= offsetX;
			}

			if (offsetY > 0) {
				result.y -= offsetY;
			}
			return result;
		}

		private Calendar getCalendar() {
			if (showcalendar == null) {
				showcalendar = Calendar.getInstance();
			}
			showcalendar.setTime(getDate());
			return showcalendar;
		}

		private int getSelectedYear() {
			return ((Integer) yearSpinner.getValue()).intValue();
		}

		private int getSelectedMonth() {
			return ((Integer) monthSpinner.getValue()).intValue();
		}

		private int getSelectedDay() {
			return ((Integer) daySpinner.getValue()).intValue();
		}

		private int getSelectedHour() {
			return ((Integer) hourSpinner.getValue()).intValue();
		}

		private int getSelectedMinite() {
			return ((Integer) minuteSpinner.getValue()).intValue();
		}

		private int getSelectedSecond() {
			return ((Integer) secondSpinner.getValue()).intValue();
		}

		private int getSelectedMillisecond() {
			return ((Integer) millisecondSpinner.getValue()).intValue();
		}

		private void flushWeekAndDay() {
			Calendar c = getCalendar();
			c.set(Calendar.DAY_OF_MONTH, 1);
			int maxDayNo = c.getActualMaximum(Calendar.DAY_OF_MONTH);
			int dayNo = 2 - c.get(Calendar.DAY_OF_WEEK);
			for (int i = 0; i < 6; i++) {
				for (int j = 0; j < 7; j++) {
					String s = "";
					if (dayNo >= 1 && dayNo <= maxDayNo)
						s = String.valueOf(dayNo);
					daysButton[i][j].setText(s);
					dayNo++;

					if (j == 0 || j == 6)
						daysButton[i][j].setForeground(weekendFontColor);
					else
						daysButton[i][j].setForeground(dateFontColor);
				}
			}
		}

		private void refreshdayspin(int currentYear, int currentMonth,
				int currentday) {
			daySpinner.setModel(getdaymodel(currentYear, currentMonth,
					currentday));
			daySpinner.updateUI();
		}

		public void stateChanged(ChangeEvent e) {
			JSpinner source = (JSpinner) e.getSource();
			Calendar c = getCalendar();
			if (source.getName().equals(HOUR)) {
				c.set(Calendar.HOUR_OF_DAY, getSelectedHour());
				setDate(c.getTime());
				return;
			}
			if (source.getName().equals(MINUTE)) {
				c.set(Calendar.MINUTE, getSelectedMinite());
				setDate(c.getTime());
				return;
			}
			if (source.getName().equals(SECOND)) {
				c.set(Calendar.SECOND, getSelectedSecond());
				setDate(c.getTime());
				return;
			}

			if (source.getName().equals(MILLISECOND)) {
				c.set(Calendar.MILLISECOND, getSelectedMillisecond());
				setDate(c.getTime());
				return;
			}
			
			if (source.getName().equals(DAY)) {
				c.set(Calendar.DAY_OF_MONTH, getSelectedDay());
				setDate(c.getTime());
				return;
			}

			if (source.getName().equals(YEAR)) {
				c.set(Calendar.YEAR, getSelectedYear());
				refreshdayspin(getSelectedYear(), getSelectedMonth(),
						getSelectedDay());
			} else if (source.getName().equals(MONTH)) {
				c.set(Calendar.MONTH, getSelectedMonth() - 1);
				refreshdayspin(getSelectedYear(), getSelectedMonth(),
						getSelectedDay());
			}
			setDate(c.getTime());
			flushWeekAndDay();
		}

		private void changeSpinnerStatus(boolean enableYearSpinner,
				boolean enableMonthSpinner, boolean enableDaySpinner,
				boolean enableHourSpinner, boolean enableMinuteSpinner,
				boolean enableSecondSpinner, boolean enableMillisecondSpinner,
				boolean enableDisplayButtons) {

			yearSpinner.setEnabled(enableYearSpinner);
			monthSpinner.setEnabled(enableMonthSpinner);
			daySpinner.setEnabled(enableDaySpinner);
			hourSpinner.setEnabled(enableHourSpinner);
			minuteSpinner.setEnabled(enableMinuteSpinner);
			secondSpinner.setEnabled(enableSecondSpinner);
			millisecondSpinner.setEnabled(enableMillisecondSpinner);
			enabledisplaybuttons(false);

		}

		public void actionPerformed(ActionEvent e) {
			String classname = e.getSource().getClass().getName();
			if (classname.equals("javax.swing.JButton")) {
				JButton source = (JButton) e.getSource();
				if (source.getText().length() == 0)
					return;

				source.setForeground(todayBackColor);
				int newDay = Integer.parseInt(source.getText());
				daySpinner.setValue(Integer.valueOf(newDay));
			} else if (classname.equals("javax.swing.JRadioButton")) {
				JRadioButton source = (JRadioButton) e.getSource();
				if (source.isSelected()) {

					switch (source.getText()) {
					case CENTURY:
						changeSpinnerStatus(false, false, false, false, false,
								false, false, false);
						break;
					case DECADE:
						changeSpinnerStatus(false, false, false, false, false,
								false, false, false);
						break;
					case YEAR:
						changeSpinnerStatus(true, false, false, false, false,
								false, false, false);
						break;

					case MONTH:
						changeSpinnerStatus(true, true, false, false, false,
								false, false, false);
						break;
					case DAY:
						changeSpinnerStatus(true, true, true, false, false,
								false, false, true);
						break;
					case HOUR:
						changeSpinnerStatus(true, true, true, true, false,
								false, false, true);
						break;
					case MINUTE:
						changeSpinnerStatus(true, true, true, true, true,
								false, false, true);
						break;
					case SECOND:
						changeSpinnerStatus(true, true, true, true, true, true,
								false, true);
						break;
					case MILLISECOND:
						changeSpinnerStatus(true, true, true, true, true, true,
								true, true);
						break;
					default:
						break;
					}

				}
			}
		}

		private void enabledisplaybuttons(boolean enable) {
			for (int i = 0; i < daysButton.length; i++) {
				for (int j = 0; j < daysButton[i].length; j++) {
					daysButton[i][j].setEnabled(enable);
				}
			}
		}

		private void refreshtime() {
			if (showcalendar == null) {
				showcalendar = getCalendar();
			}
		}

		private Action okAction = new AbstractAction(Const.OK_BTN_STR) {
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) {
				calendar = showcalendar;
				refreshtime();
				setDate(showcalendar.getTime());
				dialog.setVisible(false);
			}
		};

		private Action cancelAction = new AbstractAction(Const.CANCEL_BTN_STR) {
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) {
				dialog.setVisible(false);
			}
		};

		public void mouseClicked(MouseEvent e) {
			JButton source = (JButton) e.getSource();
			if (source.getText().length() == 0)
				return;
			source.setForeground(todayBackColor);
			int newDay = Integer.parseInt(source.getText());
			Calendar c = getCalendar();
			c.set(Calendar.DAY_OF_MONTH, newDay);
			setDate(c.getTime());
			daySpinner.setValue(Integer.valueOf(newDay));
		}

		public void mouseEntered(MouseEvent e) {
		}

		public void mouseExited(MouseEvent e) {
		}

		public void mousePressed(MouseEvent e) {
		}

		public void mouseReleased(MouseEvent e) {
		}

		private WindowListener createAppCloser() {
			return new WindowAdapter() {
				@Override
				public void windowClosing(final WindowEvent we) {
					dialog.setVisible(false);
				}
			};
		}
	}
}
